// PDP-11 Instruction set simulator.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#pragma pack(push)

#pragma pack(1)

#define	MAX_PHY_ADDR_WIDTH	64*1024

char	g_physical_memory[MAX_PHY_ADDR_WIDTH];

#define	DEFAULT_BASE_ADDRESS	0

#define	GET_PDP11_ADDR(address)	((WORD*) address - (WORD*) (&g_physical_memory))

typedef	struct _pdp11_instruction_format {

} pdp11_instruction_format;

typedef	enum _addressing_reference	{
	no_addr_reference = 0,
	addr_pc_reference,
	addr_sp_reference,
	addr_direct_addr_reference,
	addr_deferred_addr_reference,
	addr_undefine_addr_reference
} addressing_reference;

typedef	struct _program_status_word {
	union psw {
		struct _psw_bits{
			WORD	psw_carry:1;
			WORD	psw_overflow:1;
			WORD	psw_zero:1;
			WORD	psw_negative:1;
			WORD	psw_trace:1;
			WORD	psw_priority:3;
			WORD	psw_reserved:8;
		} psw_bits;
		WORD psw_all;
	};
} program_status_word;

typedef	struct _pdp11_register_set {
	WORD	R0;
	WORD	R1;
	WORD	R2;
	WORD	R3;
	WORD	R4;
	WORD	R5;
	WORD	R6;			// Register 6 is used for stack
	WORD	R7;			// Register 7 is used as program counter
	program_status_word	program_status;		// Program Status Word
} pdp11_register_set, *ppdp11_register_set;

pdp11_register_set	g_registers;


typedef struct _operation_info {
	WORD					flags;
	addressing_reference	source_mode;
	register_select			source_reg;
	addressing_reference	destination_mode;
	register_select			destination_reg;
	WORD					immediate;
} operation_info;


typedef WORD (*operation_callback) (operation_info *info);

WORD	invalid_operation(operation_info *info)
{
	UNREFERENCED_PARAMETER (info);

	return -1;
}
WORD	pdp11_mov (operation_info *info)
{

}

#define	MAX_PDP11_OPERATIONS	256

operation_callback	g_operations[MAX_PDP11_OPERATIONS] = {&arbit_pdp11, &pdp11_mov

};

void initialization_pdp11_operations ()
{
	for (int i=0; i < MAX_PDP11_OPERATIONS; i++)
	{
		g_operations[i] = &invalid_operation;
	}

	g_operations[1] = &pdp11_mov;

}

#define	INSTRUCTION_BYTE_ADDRESSABLE		0x8000

#define	INSTRUCTION_ADD_SUB_OPCODE			6

#define	INSTRUCTION_OPCODE_SPILLOVER_MASK	7

typedef enum _register_select {
	reg_select_r0=0,
	reg_select_r1,
	reg_select_r2,
	reg_select_r3,
	reg_select_r4,
	reg_select_r5,
	reg_select_r6,
	reg_select_r7,
	reg_select_psw,
	reg_select_undefined
} register_select;

typedef enum _addressing_mode {
	addressing_mode_reg=0,
	addressing_mode_indirect,
	addressing_mode_auto_incr,
	addressing_mode_deferred_auto_incr,
	addressing_mode_auto_decr,
	addressing_mode_deferred_auto_decr,
	addressing_mode_indexed,
	addressing_mode_deferred
} addressing_mode;

typedef enum _addressing_mode_r7 {
	addressing_mode_r7_imm=2,
	addressing_mode_r7_abs,
	addressing_mode_r7_rel=6,
	addressing_mode_r7_deferred
} addressing_mode_r7;

typedef struct _opcode_7 {
	WORD	microcode_addressibility:1;
	WORD	microcode_operation:3;
	WORD	microcode_sourcemode:3;
	WORD	microcode_source:3;
	WORD	microcode_destinationmode:3;
	WORD	microcode_destination:3;
} opcode_7;

typedef struct _opcode_extended {
	WORD	microcode_addressibility:1;
	WORD	microcode_operation:6;
	WORD	microcode_source:3;
	WORD	microcode_destinationmode:3;
	WORD	microcode_destination:3;
} opcode_extended;

typedef struct _opcode {
	union {
		opcode_7 _op_7;
		opcode_extended	_op_ex;
		WORD	_op_word;
	} op;
} opcode;

#pragma pack(pop)

void initialize_pdp11_state()
{
	memset(&g_registers, 0, sizeof(g_registers));
}

void	populate_register(WORD *reg_addr, WORD reg_val, addressing_reference addr_ref)
{
	*reg_addr = reg_val;


}

WORD	fetch_instruction(WORD *input_instr_stream, WORD *instr_length)
{
	populate_register(&g_registers.R7, GET_PDP11_ADDR(input_instr_stream), addr_pc_reference);

	*instr_length = 0;
	return *input_instr_stream;
}

int	start_emulation(char	*instruction_stream)
{
	WORD	current_instr = 0, *instructions = NULL, instr_length = 0;
	WORD			inst_index;
	operation_info	instr_info;

	opcode	opc;
	instructions = (WORD *) instruction_stream;

	while (1)
	{
		current_instr = fetch_instruction(instructions, &instr_length);
		opc.op._op_word = current_instr;

		//
		(operations[inst_index])(&instr_info);
		//

		//instr_length = get_instruction_length();
		current_instr += instr_length;
	}
}

#define	MAX_INPUT_SIZE	25

int _tmain(int argc, _TCHAR* argv[])
{
	int		input_char = 0;
	char	input_buffer[MAX_INPUT_SIZE], *ptr;
	WORD	load_counter = 0, program_start = 0;
	DWORD	octal_input = 0;

	load_counter = DEFAULT_BASE_ADDRESS;

	// Code is being is read from input stream stdin
	// So use input redirection method to read from file
	// It will keep reading until EOF is encountered
	do 
	{
		ptr = gets(input_buffer);

		if (input_buffer[0] != '@' || input_buffer[0] != '-')
		{
			printf("Input in bad format, exiting\n");
			break;
		}

		sscanf(input_buffer, "%o", &octal_input);

		*(WORD *)&g_physical_memory[load_counter] = (WORD) octal_input;

		load_counter += 2;

	} while (ptr != NULL);

	initialize_pdp11_state();

	start_emulation(&g_physical_memory[program_start]);
	return 0;
}

